package com.chocho.javacompileserver.service;

import com.chocho.javacompileserver.bean.Paper;
import com.chocho.javacompileserver.client.PushResultClient;
import com.chocho.javacompileserver.controller.CompileController;
import com.chocho.javacompileserver.mapper.PaperMapper;
import com.chocho.javacompileserver.util.ConsoleOutputCapturer;
import com.chocho.javacompileserver.util.UUIDUtil;
import com.chocho.javacompileserver.vo.Code;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.sql.Date;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by chocho on 2022/2/26.
 */
@Service
public class StudentService {

    private Logger logger = LoggerFactory.getLogger(StudentService.class);

    @Autowired
    private PushResultClient pushResultClient;
    @Autowired
    private PaperMapper paperDao;

    public Map<String, Object> compileJava (Code code){
        //处理代码
        //String code = CodeStandardUtil.getCode(codestr.getSource());
        //处理参数

        Map<String, Object> map = new HashMap<>();
        try{
            // 初始化诊断收集器
            DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
            // 编译准备
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            JavaFileManager javaFileManager = new MyJavaFileManager(compiler.getStandardFileManager(collector, null, null));

            List<String> options = new ArrayList<>();
            options.add("-target");
            options.add("1.8");

            Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s*");
            Matcher matcher = CLASS_PATTERN.matcher(code.getSource());
            String cls;
            if(matcher.find()){
                cls = matcher.group(1);
            } else{
                throw new IllegalArgumentException("No such class name in " + code);
            }

            //编译
            JavaFileObject javaFileObject = new MyJavaFileObject(cls, code.getSource());
            Boolean result = compiler.getTask(null, javaFileManager, collector, options, null, Arrays.asList(javaFileObject)).call();
            List<Diagnostic<? extends JavaFileObject>> diagnosticList = collector.getDiagnostics();
            // 加载到Class<?>对象
            ClassLoader classloader = new MyClassLoader();
            Class<?> clazz  = classloader.loadClass(cls);
            // 创建实例
            Object instance = clazz.newInstance();
            Method mainMethod = clazz.getMethod("main", String[].class);
            ConsoleOutputCapturer capturer = new ConsoleOutputCapturer();
            logger.info("=========>开始捕捉控制台内容");
            capturer.start();
            //执行main方法
            mainMethod.invoke(instance, new Object[]{new String[]{"1"}});
            String res = capturer.stop();
            logger.info("=========>结束捕捉控制台内容：" + res);

            if(diagnosticList.size() != 0){
                List<String> errorList = new ArrayList<>();
                for(Diagnostic<? extends JavaFileObject> c : diagnosticList){
                    errorList.add(c.toString());
                }
                map.put("code", "400");
                map.put("msg", "编译错误");
                map.put("detailMsg", errorList);
            }else {
                map.put("code", "200");
                map.put("res", res);
            }
        }catch (Exception e){
            //异常信息返回给前端
            e.printStackTrace();
            map.put("code", "400");
            map.put("msg", "运行错误");
            map.put("detailMsg", e.getMessage());
        } finally {
            String userId = code.getUserId();
            String questionId = code.getQuestionId();

            if (userId == null || userId.equals("")) {//游客
                map.put("questionId", questionId);
                pushResultClient.pushResult(map);
            } else{//系统用户
                Paper paper = new Paper();
                paper.setPaperId(UUIDUtil.getUUID());
                paper.setUserId(userId);
                paper.setCreateTime(new Date(System.currentTimeMillis()));
                String status = (String)map.get("code");
                paper.setPaperStatus((status != null && "200".equals(status))
                        ? 1 : ("400".equals(status)) ? 3 : 2);
                paper.setPaperScore(0);
                paper.setPaperAnswer(code.getSource());
                paper.setQuestionId(code.getQuestionId());
                paper.setMemoryLimit(0);
                paper.setTimeLimit(0);
                paperDao.insert(paper);

                map.put("questionId", questionId);
                pushResultClient.pushResult(map);
            }
        }

        return map;
    }

    private static Map<String, JavaFileObject> fileObjects = new ConcurrentHashMap<String, JavaFileObject>();

    public static class MyClassLoader extends ClassLoader {

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            JavaFileObject fileObject = fileObjects.get(name);
            if(fileObject != null){
                byte[] bytes = ((MyJavaFileObject)fileObject).getCompiledBytes();
                return defineClass(name, bytes, 0, bytes.length);
            }
            try{
                return ClassLoader.getSystemClassLoader().loadClass(name);
            } catch (Exception e){
                return super.findClass(name);
            }
        }
    }

    public static class MyJavaFileObject extends SimpleJavaFileObject {
        private String source;
        private ByteArrayOutputStream outPutStream;


        public MyJavaFileObject(String name, String source) {
            super(URI.create("String:///" + name + Kind.SOURCE.extension), Kind.SOURCE);
            this.source = source;
        }

        public MyJavaFileObject(String name, Kind kind){
            super(URI.create("String:///" + name + kind.extension), kind);
            source = null;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors){
            if(source == null){
                throw new IllegalArgumentException("source == null");
            }
            return source;
        }

        @Override
        public OutputStream openOutputStream() throws IOException {
            outPutStream = new ByteArrayOutputStream();
            return outPutStream;
        }

        public byte[] getCompiledBytes(){
            return outPutStream.toByteArray();
        }
    }

    public static class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
        protected MyJavaFileManager(JavaFileManager fileManager) {
            super(fileManager);
        }

        @Override
        public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
            JavaFileObject javaFileObject = fileObjects.get(className);
            if(javaFileObject == null){
                super.getJavaFileForInput(location, className, kind);
            }
            return javaFileObject;
        }

        @Override
        public JavaFileObject getJavaFileForOutput(Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
            JavaFileObject javaFileObject = new MyJavaFileObject(qualifiedClassName, kind);
            fileObjects.put(qualifiedClassName, javaFileObject);
            return javaFileObject;
        }
    }
}